Paracol = function() {

    var extensionPoints = ParacolUtils.makeMixable({
        transformServerMessageNode: function(node, ctx) {
            return node;
        },
        getNameValuePairFromField: function (field) {
            //alert(field.name + "=" + field.value);
            //return [field.name, field.value || field.getValue()];
            return [field.name, field.getValue()];
        }
    });

    // mix in the extensions
    extensionPoints.MixWithAll(arguments);

    var xmlhttp = false;

    function initXmlHttp() {
        /*@cc_on @*/
        /*@if (@_jscript_version >= 5)
         // JScript gives us Conditional compilation, we can cope with old IE versions.
         // and security blocked creation of the objects.
         try {
         xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
         } catch (e) {
         try {
         xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
         } catch (E) {
         xmlhttp = false;
         }
         }
         @end @*/
        if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
            try {
                xmlhttp = new XMLHttpRequest();
            } catch (e) {
                xmlhttp = false;
            }
        }
        if (!xmlhttp && window.createRequest) {
            try {
                xmlhttp = window.createRequest();
            } catch (e) {
                xmlhttp = false;
            }
        }
        return xmlhttp
    }

    initXmlHttp();

    function createCookieResponse(formItems) {
        var cookieResponse = {};
        for (var i = 0; i < formItems.length; i++) {
            var field = formItems[i];
            var nameValuePair = extensionPoints.getNameValuePairFromField(field);
            cookieResponse[nameValuePair[0]] = nameValuePair[1];
        }

        return cookieResponse;
    }

    var errorHandler = function(errorMessage) {
        alert(errorMessage.message + "(status:" + errorMessage.status + ")");
    };

    function httpResponseHandler(responseReceiver, ctx) {
        return function() {
            if (xmlhttp.readyState == 4) {

                if (xmlhttp.status >= 400) {
                    errorHandler({
                        msgType: "Error",
                        message: xmlhttp.statusText,
                        status: xmlhttp.status
                    }, ctx);
                } else {
                    var jsonResponse = xmlhttp.responseText;

                    var message = JSON.parse(jsonResponse, function(key, value) {
                        return extensionPoints.transformServerMessageNode(value, ctx);
                    });

                    if (responseReceiver != null) {
                        if (responseReceiver.onResponse) {
                            // generic response handler
                            responseReceiver.onResponse(message, ctx);
                        } else if (message.msgType == "Error") {
                            errorHandler(message, ctx);
                        } else if (message.msgType == "EditCookie") {
                            responseReceiver.onEditCookie(message, ctx);
                        } else if (message.msgType == "ResultStarterReady") {
                            responseReceiver.onResultStarterReady(message, ctx);
                        } else if (message.msgType == "Result") {
                            responseReceiver.onResult(message, ctx);
                        } else if (message.msgType == "Hints") {
                            responseReceiver.onHints(message, ctx);
                        } else if (message.msgType == "End") {
                            responseReceiver.onEnd(message, ctx);
                        } else if (message.msgType == "EditPreConfigCookies") {
                            responseReceiver.onEditPreConfigCookies(message, ctx);
                        } else if (message.msgType == "EditAllPreConfigCookies") {
                            responseReceiver.onEditAllPreConfigCookies(message, ctx);
                        } else if (message.msgType == "PreConfigCookiePropertyUpdated") {
                            responseReceiver.onPreConfigCookiePropertyUpdated(message, ctx);
                        } else if (message.msgType == "PreConfigCookiePropertyUpdateError") {
                            responseReceiver.onPreConfigCookiePropertyUpdated(message, ctx);
                        } else if (message.msgType == "ProjectSaved") {
                            responseReceiver.onProjectSaved(message, ctx);
                        } else if (message.msgType == "MetaProjectEntries") {
                            responseReceiver.onMetaProjectEntries(message, ctx);
                        } else if (message.msgType == "ProjectEntryAdded") {
                            responseReceiver.onProjectEntryAdded(message, ctx);
                        } else if (message.msgType == "ProjectEntryRemoved") {
                            responseReceiver.onProjectEntryRemoved(message, ctx);
                        } else if (message.msgType == "ExtensionMoved") {
                            responseReceiver.onExtensionMoved(message, ctx);
                        }
                    } else {
                        if (message.msgType == "Error") {
                            errorHandler(message, ctx);
                        }
                    }
                    // notify listeners
                    for (var i = 0; i < httpListeners.length; i++) {
                        httpListeners[i].onResponseReceived(message)
                    }
                }
            }
        }
    }

    function customResponseHandler(responseReceiver, ctx) {
        return function() {
            if (xmlhttp.readyState == 4) {

                if (xmlhttp.status >= 400) {
                    errorHandler({
                        msgType: "Error",
                        message: xmlhttp.statusText,
                        status: xmlhttp.status
                    }, ctx);

                } else {


                    var response = xmlhttp.responseText;

                    if (responseReceiver != null) {
                        responseReceiver.onResponse(response, ctx);
                    }

                    // notify listeners
                    for (var i = 0; i < httpListeners.length; i++) {
                        httpListeners[i].onResponseReceived(response)
                    }
                }
            }
        }
    }

    function postCustomMessage(method, url, responseReceiver, message, ctx) {
        xmlhttp.open(method, url, true);
        xmlhttp.onreadystatechange = customResponseHandler(responseReceiver, ctx);
        xmlhttp.send(message);

        // notify listeners
        for (var i = 0; i < httpListeners.length; i++) {
            httpListeners[i].onRequestSent(message)
        }
    }

    function postMessage(message, responseReceiver, cookieUrl, ctx) {
        xmlhttp.open("POST", cookieUrl, true);
        xmlhttp.onreadystatechange = httpResponseHandler(responseReceiver, ctx);
        xmlhttp.send(JSON.stringify(message));

        // notify listeners
        for (var i = 0; i < httpListeners.length; i++) {
            httpListeners[i].onRequestSent(message)
        }
    }

    /// messages

    function messageStartEdit(project, page) {
        return {
            msgType: "StartEdit",
            projectName: project,
            pageName: page
        }
    }

    function messageConsumeCookie(fields) {
        return {
            msgType: "ConsumeCookie",
            updatedValues: createCookieResponse(fields)
        }
    }

    function messageRefresh() {
        return {
            msgType: "Refresh"
        }
    }

    function messageGetResult() {
        return {
            msgType: "GetResult"
        }
    }

    function messageGetHints(subj) {
        return {
            msgType: "GetHints",
            subject: subj
        }
    }

    function messageOpenHint(hint, subj) {
        var hintValue;
        var hintType = "unknown";
        // todo: Is it really necessary? If it is move it to extjsMessageFilters.js.
        switch (hint.getXType()) {
            case "menucheckitem":
                hintValue = hint.checked;
                hintType = "Boolean";
                break;
        }

        return {
            msgType: "OpenHint",
            id: hint.id,
            subject: subj,
            type: hintType,
            value: hintValue
        }
    }

    function messageCloseFlow() {
        return {
            msgType: "CloseFlow"
        }
    }

    function messageExecute() {
        return {
            msgType: "Execute"
        }
    }

    function messageUnconsumeCookie() {
        return {
            msgType: "Unconsume"
        }
    }

    function messageGetPreConfigCookies(page) {
        return {
            msgType: "GetPreConfigCookies",
            pageName: page
        }
    }

    function messageAllGetPreConfigCookies(scenarioName) {
        return {
            msgType: "GetAllPreConfigCookies",
            scenario: scenarioName
        }
    }

    //function messageUpdatePreConfigCookieProperty(page, cindex, propName, val) {
    function messageUpdatePreConfigCookieProperty(page, cindex, propName, field) {
        var pair = createCookieResponse([field]);
        return {
            msgType: "UpdatePreConfigCookieProperty",
            pageName: page,
            cookieIndex: cindex,
            propertyName: propName,
            value: pair[propName]
        }
    }

    function messageSaveProject() {
        return {
            msgType: "SaveProject"
        }
    }

    function messageGetMetaProjectEntries() {
        return {
            msgType: "GetMetaProjectEntries"
        }
    }

    function messageAddProjectEntry(metaPageName, pageName, scenario, preconfigured) {
        return {
            msgType: "AddProjectEntry",
            metaPageName: metaPageName,
            pageName: pageName,
            scenario: scenario,
            preconfigured: preconfigured
        }
    }

    function messageRemoveEntry(pageName) {
        return {
            msgType: "RemoveEntry",
            pageName: pageName
        }
    }

    function messageMoveExtension(extId, delta) {
        return {
            msgType: "MoveExtension",
            extId: extId,
            delta: delta
        }
    }

    var httpListeners = [];

    return {

        dispatcherUrl: 'disp/',

        setErrorHandler: function(handler) {
            errorHandler = handler;
        },

        addHttpListener: function(listener) {
            httpListeners[httpListeners.length] = listener;
        },

        postCustomMessage: function(method, url, responseReceiver, message, ctx) {
            postCustomMessage(method, url, responseReceiver, message, ctx)
        },

        postCustomJsonMessage: function(method, url, responseReceiver, message, ctx) {
            postMessage(message, responseReceiver, url, ctx)
        },

        startEdit: function(projectName, pageName, responseReceiver, ctx) {
            postMessage(messageStartEdit(projectName, pageName), responseReceiver, this.dispatcherUrl, ctx);
        },

        refresh: function(responseReceiver, ctx) {
            postMessage(messageRefresh(), responseReceiver, this.dispatcherUrl, ctx);
        },

        consumeCookie: function(cookieFields, responseReceiver, ctx) {
            postMessage(messageConsumeCookie(cookieFields), responseReceiver, this.dispatcherUrl, ctx);
        },

        unconsumeCookie: function(responseReceiver, ctx) {
            postMessage(messageUnconsumeCookie(), responseReceiver, this.dispatcherUrl, ctx);
        },

        getResult: function(responseReceiver, ctx) {
            postMessage(messageGetResult(), responseReceiver, this.dispatcherUrl, ctx);
        },

        getHints: function(responseReceiver, subject, ctx) {
            postMessage(messageGetHints(subject), responseReceiver, this.dispatcherUrl, ctx);
        },

        openHint: function(responseReceiver, hint, subj, ctx) {
            postMessage(messageOpenHint(hint, subj), responseReceiver, this.dispatcherUrl, ctx);
        },

        closeFlow: function(responseReceiver, ctx) {
            postMessage(messageCloseFlow(), responseReceiver, this.dispatcherUrl, ctx);
        },

        execute: function(responseReceiver, ctx) {
            postMessage(messageExecute(), responseReceiver, this.dispatcherUrl, ctx);
        },

        getPreConfigCookies: function(pageName, responseReceiver, ctx) {
            postMessage(messageGetPreConfigCookies(pageName), responseReceiver, this.dispatcherUrl, ctx);
        },

        getAllPreConfigCookies: function(scenario, responseReceiver, ctx) {
            postMessage(messageAllGetPreConfigCookies(scenario), responseReceiver, this.dispatcherUrl, ctx);
        },

        updatePreConfigCookieProperty: function(page, cindex, propName, val, responseReceiver, ctx) {
            postMessage(messageUpdatePreConfigCookieProperty(page, cindex, propName, val), responseReceiver, this.dispatcherUrl, ctx);
        },

        saveProject: function(responseReceiver, ctx) {
            postMessage(messageSaveProject(), responseReceiver, this.dispatcherUrl, ctx);
        },

        getMetaProjectEntries: function(responseReceiver, ctx) {
            postMessage(messageGetMetaProjectEntries(), responseReceiver, this.dispatcherUrl, ctx);
        },

        addProjectEntry: function(metaPageName, pageName, scenario, preconfigured, responseReceiver, ctx) {
            postMessage(messageAddProjectEntry(metaPageName, pageName, scenario, preconfigured), responseReceiver, this.dispatcherUrl, ctx);
        },

        removeEntry: function(pageName, responseReceiver, ctx) {
            postMessage(messageRemoveEntry(pageName), responseReceiver, this.dispatcherUrl, ctx);
        },

        moveSelectedExtension: function(extId, delta, responseReceiver, ctx) {
            postMessage(messageMoveExtension(extId, delta), responseReceiver, this.dispatcherUrl, ctx);
        }
    }
};


ParacolUtils = function() {

    function ProxyFunction(context, method) {
        this.proxy = function() {
            return method.apply(context, arguments);
        }
    }

    return {

        makeMixable: function(obj) {
            obj.MixWith = function(mixin) {
                //for (var e = 0; e < arguments.length; e++) {
                //var mixin = arguments[e];

                if (this == mixin) {
                    alert("Error - cannot mix with itself");
                    return;
                }
                var next = {}; // the container for all overridden functions
                for (var prop in mixin) {
                    if (this[prop]) {
                        if (typeof this[prop] == "function" && prop != "MixWith") {
                            var superProxyFn = new ProxyFunction(this, this[prop]);
                            next[prop] = superProxyFn.proxy;
                            var subProxyFn = new ProxyFunction(mixin, mixin[prop]);
                            this[prop] = subProxyFn.proxy;
                        }
                    }
                }
                mixin._next_ = next;
                //}
            };

            obj.MixWithAll = function(mixins) {
                for (var e = 0; e < mixins.length; e++) {
                    this.MixWith(mixins[e]);
                }
            };

            return obj;
        },

        object: function object(o) {
            function F() {
            }

            F.prototype = o;
            return new F();
        },

        createExtensionFunction: function(defaultScope, extension) {
            return function() {
                var fname = arguments[0];
                var extFn = extension && extension[fname];
                if (extFn) {
                    var extFnArgs = [];
                    for (var i = 2; i < arguments.length; i++) {
                        extFnArgs[i - 2] = arguments[i];
                    }
                    // the default function is put at the end of the arguments
                    extFnArgs[extFnArgs.length] = arguments[1];
                    return extFn.apply(extension, extFnArgs);
                } else {
                    var defaultFnArgs = [];
                    var defaultFn = arguments[1];
                    for (var j = 2; j < arguments.length; j++) {
                        defaultFnArgs[j - 2] = arguments[j];
                    }
                    return defaultFn.apply(defaultScope, defaultFnArgs);
                }
            }
        }
    };
}();

//Function.prototype.DeriveFrom = function(fnNext) {
//    var prop;
//    if (this == fnNext) {
//        alert("Error - cannot mix with self");
//        return;
//    }
//    for (prop in fnNext.prototype) {
//        if (!this.prototype[prop]) {
//            this.prototype[prop] = fnNext.prototype[prop];
//        }
//    }
//
//    this.prototype._next_ = fnNext.prototype;
//    this.prototype._fnNext_ = fnNext;
//};
//
//function ProxyFunction(context, method) {
//    this.proxy = function() {
//        return method.apply(context, arguments);
//    }
//}
//
//var _inject_ = {};
//


//function A() {
//}
//
//A.prototype = {
//    x: 1,
//    doIt: function(x) {
//        alert("doIt1(" + x + ")");
//        return x + x;
//    },
//    doThat: function() {
//        alert("doThat1");
//    }
//};
//
//function B() {
//}
//
//B.prototype = {
//    y: 2,
//    doIt: function(x) {
//        alert("before B.doIt:" + this.y);
//        var res = this._next_.doIt(4 * x) - 3;
//        alert("after B.doIt:" + this.y);
//        return res;
//    },
//    doThat: function() {
//        alert("before B.doThat:" + this.y);
//        this._next_.doThat();
//        alert("after B.doThat:" + this.y);
//    }
//};
//
//function C() {
//}
//
//C.prototype = {
//    z: 3,
//    x: _inject_,
//    doIt: function(x) {
//        alert("before C.doIt:" + this.z);
//        var res = this._next_.doIt(3 * x) - 2;
//        alert("after C.doIt:" + this.z);
//        return res;
//    }
//};
//
//function D() {
//}
//
//D.prototype = {
//    u: 4,
//    doIt: function(x) {
//        alert("before D.doIt:" + this.u);
//        var res = this._next_.doIt(2 * x) - 1;
//        alert("after D.doIt:" + this.u);
//        return res;
//    }
//    ,
//    doThat: function() {
//        alert("before D.doThat:" + this.u);
//        this._next_.doThat();
//        alert("after D.doThat:" + this.u);
//    }
//};
//
//var a = new A();
//var b = new B();
//var c = new C();
//var d = new D();
////    c.MixWith(d);
////    b.MixWith(c);
////    a.MixWith(b);
////    a.doThat();
//a.MixWith(b);
//a.MixWith(c);
//a.MixWith(d);
//alert("result:" + a.doIt(1));
//
//
////    B.DeriveFrom(A);
////
////    var b1 = new B();
////    var b2 = new B();
////    b1.x = 1;
////    b2.x = 2;
////    alert("b1.x=" + b1.x);
////    alert("b2.x=" + b2.x);
////    alert("b2.y=" + b2.y);
////    b1.doIt();
////    b2.doThat();
//
////var o1 = ParacolUtils.object(proto);
